using UnityEngine;
using Prota;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
using Prota.Unity;
using System.Collections.Generic;
using System.Linq;

namespace Prota.VisualEffect
{

    public class ProtaSurfaceRenderFeature : ScriptableRendererFeature
    {
        Dictionary<RenderPassEvent, ProtaSurfaceRenderPass> passes = null;
        
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            foreach (var pass in passes)
            {
                renderer.EnqueuePass(pass.Value);
            }
        }

        public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
        {
            if(renderingData.cameraData.camera.cameraType != CameraType.Game) return;
            foreach (var pass in passes)
            {
                pass.Value.finalRT = renderer.cameraColorTargetHandle;
            }
        }

        public override void Create()
        {
            if(passes == null) passes = new Dictionary<RenderPassEvent, ProtaSurfaceRenderPass>();
            else return;
            passes.Add(RenderPassEvent.BeforeRendering, new ProtaSurfaceRenderPass(RenderPassEvent.BeforeRendering));
            passes.Add(RenderPassEvent.BeforeRenderingOpaques, new ProtaSurfaceRenderPass(RenderPassEvent.BeforeRenderingOpaques));
            passes.Add(RenderPassEvent.AfterRenderingOpaques, new ProtaSurfaceRenderPass(RenderPassEvent.AfterRenderingOpaques));
            passes.Add(RenderPassEvent.BeforeRenderingSkybox, new ProtaSurfaceRenderPass(RenderPassEvent.BeforeRenderingSkybox));
            passes.Add(RenderPassEvent.AfterRenderingSkybox, new ProtaSurfaceRenderPass(RenderPassEvent.AfterRenderingSkybox));
            passes.Add(RenderPassEvent.BeforeRenderingTransparents, new ProtaSurfaceRenderPass(RenderPassEvent.BeforeRenderingTransparents));
            passes.Add(RenderPassEvent.AfterRenderingTransparents, new ProtaSurfaceRenderPass(RenderPassEvent.AfterRenderingTransparents));
            passes.Add(RenderPassEvent.BeforeRenderingPostProcessing, new ProtaSurfaceRenderPass(RenderPassEvent.BeforeRenderingPostProcessing));
            passes.Add(RenderPassEvent.AfterRenderingPostProcessing, new ProtaSurfaceRenderPass(RenderPassEvent.AfterRenderingPostProcessing) { isPreviewPass = true });
            passes.Add(RenderPassEvent.AfterRendering, new ProtaSurfaceRenderPass(RenderPassEvent.AfterRendering));
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }




    [ExecuteAlways]
    public class ProtaSurfaceRenderPass : ScriptableRenderPass
    {
        static bool cachedThisFrame = false;
        static HashMapList<RenderPassEvent, SurfaceRenderer> all = new();
        
        public RTHandle finalRT;
        
        public bool isPreviewPass = false;
        
        public ProtaSurfaceRenderPass(RenderPassEvent e)
        {
            this.renderPassEvent = e;
        }
        
        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            base.OnCameraSetup(cmd, ref renderingData);
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            base.Configure(cmd, cameraTextureDescriptor);
            
            if(!cachedThisFrame.SwapSet(true))
            {
                foreach(var s in SurfaceRenderer.all)
                {
                    all.AddElement(s.renderPassEvent, s);
                }
            }
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            DrawRenderers(context, ref renderingData);
            DrawPreview(context, ref renderingData);
        }
        
        void DrawRenderers(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            if(renderingData.cameraData.camera.cameraType != CameraType.Game) return;
            
            if(!all.ContainsKey(renderPassEvent)) return;
            foreach (var s in all[renderPassEvent])
            {
                s.PrepareRenderTexture();
                s.ResizeRenderTexture();
                s.PrepareRendererList();
                
                using(context.WithCommandBuffer(out var cmd))
                {
                    cmd.name = "ProtaSurface: Draw";
                    cmd.SetRenderTarget(s.renderTexture);
                    if(s.shouldClear) cmd.ClearRenderTarget(true, true, s.clearColor);
                    cmd.SetViewProj(s.RectTransform(), s.zNear, s.zFar);
                    
                    foreach(var rd in s.rendererList)
                    {
                        cmd.DrawRenderer(rd, rd.sharedMaterial, 0, 0);
                    }
                }
            }
        }
        
        void DrawPreview(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            if(this.isPreviewPass)
            {
                foreach(var s in SurfaceRenderer.all)
                {
                    if(!s.preview) continue;
                    using(context.WithCommandBuffer(out var cmd))
                    {
                        cmd.name = "ProtaSurface: Preview";
                        cmd.Blit(s.renderTexture, finalRT);
                    }
                }
            }
        }
        
        public override void FrameCleanup(CommandBuffer cmd)
        {
            if(cachedThisFrame.SwapSet(false)) all.Clear();
        }

    }
    
}


